package com.star.mall.base.helper;

import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.Objects;
import java.util.function.Consumer;

/**
 * redis锁
 * @author StarQ
 * @since 2021-08-12
 */
@Component
public class CommonRedisLockHelper {

    @Resource
    RedisTemplate<String, Object> redisTemplate;

    public static final String LOCK_PREFIX = "redis_lock_";

    public static final long LOCK_EXPIRE = 3000;

    public boolean lock(String key) {
        String lockKey = LOCK_PREFIX + key;
        return redisTemplate.execute((RedisCallback<Boolean>) redisConnection -> {
            long expireAt = System.currentTimeMillis() + LOCK_EXPIRE + 1;

            if (redisConnection.setNX(lockKey.getBytes(), String.valueOf(expireAt).getBytes())) {
                return true;
            }else {
                byte[] value = redisConnection.get(lockKey.getBytes());
                if (Objects.nonNull(value) && value.length > 0) {
                    long expireTime = Long.parseLong(new String(value));
                    if (expireTime < System.currentTimeMillis()) {
                        byte[] oldValue = redisConnection.getSet(lockKey.getBytes(), String.valueOf(System.currentTimeMillis() + LOCK_EXPIRE + 1).getBytes());
                        return Long.parseLong(new String(oldValue)) < System.currentTimeMillis();
                    }
                }
            }
            return false;
        });

    }

    public void deleteLock(String key) {
        String lockKey = LOCK_PREFIX + key;
        redisTemplate.execute((RedisCallback<Boolean>) redisConnection -> {
            redisConnection.del(lockKey.getBytes());
            return true;
        });
    }

    public void execute(String key, Consumer<String> consumer) {
        boolean status = lock(key);
        if (status) {
            consumer.accept(key);
            deleteLock(key);
        } else {
            int failCount = 1;
            while (failCount <= 10) {
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if (lock(key)) {
                    consumer.accept(key);
                    deleteLock(key);
                    return;
                } else {
                    failCount++;
                }
            }
            throw new RuntimeException("现在创建的人太多了, 请稍等再试");
        }
    }

}
